package com.zhongli.devplatform.controller;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zhongli.devplatform.bean.Res;
import com.zhongli.devplatform.component.JwtTokenUtil;
import com.zhongli.devplatform.component.qq.QQAuthUtil;
import com.zhongli.devplatform.component.qq.QQClientConfig;
import com.zhongli.devplatform.component.wx.WeChatAuthUtil;
import com.zhongli.devplatform.constant.Consts;
import com.zhongli.devplatform.dto.UserBindDTO;
import com.zhongli.devplatform.dto.user.UserLoginDTO;
import com.zhongli.devplatform.po.user.ReadUserPO;
import com.zhongli.devplatform.security.qq.QQAuthenticationToken;
import com.zhongli.devplatform.security.qq.QQUserInfo;
import com.zhongli.devplatform.security.weixin.WeixinAuthenticationToken;
import com.zhongli.devplatform.service.imp.user.ReadUserService;
import com.zhongli.devplatform.service.imp.user.UserRegisterService;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.TimeUnit;

/**
 * @author WuXiCheng
 * @version 1.0
 */
@RestController
@RequestMapping("/auth")
@Slf4j
public class UserAuthenticationController {

    @Autowired
    private WeChatAuthUtil weChatAuthUtil;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ReadUserService userService;

    @Value("${token.expirationMilliSeconds}")
    private long expirationMilliSeconds;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private UserRegisterService userRegisterService;

    @Autowired
    private QQAuthUtil authUtil;

    /**
     * 微信登录认证
     * @author WuXiCheng
     * @return
     * @throws Exception
     */
    @ApiOperation("微信登录认证")
    @PostMapping("/wx/login")
    public Res weChatLogin(@RequestBody UserLoginDTO userLoginDTO) throws Exception {
        String accessToken = userLoginDTO.getAccessToken();
        String openId = userLoginDTO.getOpenid();
        try {
            String infoUrl = weChatAuthUtil.getUserInfoUrl(accessToken,openId);
            //获取微信用户信息
            JSONObject wxUserInfo = WeChatAuthUtil.doGetJson(infoUrl);
            //注册微信用户
            Boolean isRegister = userRegisterService.wxRegister(wxUserInfo,userLoginDTO.getRole());
            if (isRegister){
                return new Res<>(200,"微信用户已注册,前往绑定");
            }
        } catch (Exception e) {
            log.error("微信登录失败【" + e.getMessage() + "】");
            return Res.not_ok("登录失败");
        }
        //获取已绑定用户token
        final String jwtToken = getToken(openId,1);
        return  new Res<>(0,"登录成功", jwtToken);
    }

    /**
     * qq登录认证
     * @return
     * @throws Exception
     */
    @ApiOperation("qq登录认证")
    @PostMapping("/qq/login")
    public Res qqLogin(@RequestBody UserLoginDTO userLoginDTO) throws Exception {
        String accessToken = userLoginDTO.getAccessToken();
        String openId = userLoginDTO.getOpenid();
        try {
            //获取qq用户信息
            QQUserInfo qqUserInfo = authUtil.getUserInfo(accessToken, openId);
            qqUserInfo.setOpenId(openId);
            Boolean isRegister = userRegisterService.qqRegister(qqUserInfo, userLoginDTO.getRole());
            if (isRegister){
                return new Res<>(200,"QQ用户已注册,请前往绑定");
            }
        } catch (Exception e) {
            log.error("QQ登录失败【" + e.getMessage() + "】");
        }
        //获取已绑定用户token
        final String jwtToken = getToken(openId,2);

        return new Res<>(0,"登录成功",jwtToken);
    }

    /**
     * 用户绑定
     * @param userBindDTO
     * @return
     */
    @ApiOperation(value = "用户绑定")
    @PostMapping("/bindUser")
    private Res bindUser(@RequestBody UserBindDTO userBindDTO){
        // 验证手机号与验证码
        if (verifyPhoneCode(userBindDTO.getPhone(), userBindDTO.getCode())) {
            return Res.not_ok("验证码不正确，或已过期");
        }else {
            // 删除该验证码
            stringRedisTemplate.delete(Consts.PHONE_CODE + userBindDTO.getPhone());
        }
        try {
            Boolean status = userRegisterService.bindUser(userBindDTO);
            if (status){
                final String jwtToken = getToken(userBindDTO.getOpenId(),userBindDTO.getType());
                return new Res<>(0,"绑定成功",jwtToken);
            }
        }catch (Exception e){
            log.error("绑定失败【" + e.getMessage() + "】");
        }
        return new Res<>(1,"绑定失败");
    }

    /**
     * 获取已绑定用户的token
     * @param openId
     * @return
     * @throws Exception
     */
    private String getToken(String openId,Integer type) throws Exception {
        //认证已注册的用户认证
        authenticate(openId,type);
        String accountNum;
        switch (type){
            case 1:
                accountNum = userService.getOne(new QueryWrapper<ReadUserPO>().eq("weixin_id", openId)).getAccountNum();
                break;
            case 2:
                accountNum = userService.getOne(new QueryWrapper<ReadUserPO>().eq("qq_id", openId)).getAccountNum();
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + type);
        }
        final UserDetails userDetails = userDetailsService
                .loadUserByUsername(accountNum);
        //封装redis集合
        String key = Consts.USER_TOKEN + userDetails.getUsername();
        //获取token
        final String jwtToken = jwtTokenUtil.generateToken(userDetails);
        //放入redis
        stringRedisTemplate.opsForValue().set(key,jwtToken, expirationMilliSeconds, TimeUnit.MILLISECONDS);

        return jwtToken;
    }

    /**
     * 检查验证码是否有效
     *
     * @param phone
     * @param code
     * @return
     */
    private boolean verifyPhoneCode(String phone, String code) {
        String cacheCode = stringRedisTemplate.opsForValue().get(Consts.PHONE_CODE + phone);
        return StrUtil.isBlank(cacheCode) || !StrUtil.equals(cacheCode, code);
    }


    /**
     * 获取微信用户信息
     * @param accessToken
     * @param openId
     * @return
     */
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "accessToken", value = "微信认证过后的accessToken", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "openId", value = "微信用户唯一openId",required = true,dataType = "String")
    })
    @ApiOperation(value = "获取微信用户信息")
    @GetMapping("/wx/getUserInfo")
    public Res getUserInfo(@RequestParam("accessToken") String accessToken,@RequestParam("openId") String openId){
        return userRegisterService.getWxUserInfo(accessToken,openId);
    }

    /**
     * Security认证
     * @param openId
     * @throws Exception
     */
    private void authenticate(String openId,Integer type) throws Exception {
        try {
            switch (type){
                case 1:
                    authenticationManager.authenticate(new WeixinAuthenticationToken(openId));
                    break;
                case 2:
                    authenticationManager.authenticate(new QQAuthenticationToken(openId));
            }
        } catch (DisabledException e) {
            throw new Exception("USER_DISABLED", e);
        } catch (BadCredentialsException e) {
            throw new Exception("INVALID_CREDENTIALS", e);
        }
    }
}
